ECS(Fargate)をプライベートサブネットで使うときに必要なエンドポイントを、Terraformで作れるようにしてみた
こんにちは、ゲームソリューション部のsoraです。
今回は、ECS(Fargate)をプライベートサブネットで使うときに必要なエンドポイントを、Terraformで作れるようにしてみたことについて書いていきます。
はじめに
ECS(Fargete)をプライベートサブネットで使うとき、必須でないものもありますが、以下のエンドポイントが必要になります。
- 必須
com.amazonaws.region.ecr.dkr
com.amazonaws.region.ecr.api
com.amazonaws.region.s3(Gateway)
- 必須でない
com.amazonaws.region.logs
com.amazonaws.region.secretsmanager
com.amazonaws.region.ssm
com.amazonaws.region.ssmmessages
com.amazonaws.region.s3(Gateway)
以外のエンドポイントは、放置しておくと課金が発生します。
検証で少し使いたいときも作成して削除してと面倒なため、上記エンドポイントをTerraformで簡単に作れるようにしたいというのが目的です。
前提
エンドポイントを利用するVPC・サブネット、紐づけるルートテーブルは作成済みであるとします。
(ちなみに、importブロックを消せば新規作成でもできます。)
ソースコード
以下Terraformのコードです。
検証用で利用することを想定しているため、tfstate
ファイルはローカルに配置します。
既存のリソースをインポートする部分は、利用する環境に合わせて入れてください。
terraform {
# AWSプロバイダーのバージョン指定
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.60.0"
}
}
backend "local" {
path = "soratest.tfstate"
}
}
provider "aws" {
region = "ap-northeast-1"
}
# 既存リソースのimport(環境によって変更する箇所)
## VPC
import {
to = aws_vpc.main
id = "vpc-xxxxxxxx"
}
resource "aws_vpc" "main" {
cidr_block = "xx.xx.xx.xx/16"
tags = {
Name = "sora-vpc"
}
lifecycle {
prevent_destroy = true
}
}
## Subnet
import {
to = aws_subnet.private
id = "subnet-xxxxxxxx"
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "xx.xx.xx.xx/20"
tags = {
Name = "sora-private-1a"
}
lifecycle {
prevent_destroy = true
}
}
## ルートテーブル
import {
to = aws_route_table.private
id = "rtb-xxxxxxxx"
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "sora-rtb-private-1a"
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_vpc_endpoint_route_table_association" "private_s3" {
vpc_endpoint_id = aws_vpc_endpoint.s3.id
route_table_id = aws_route_table.private.id
depends_on = [aws_vpc_endpoint.s3]
}
# リソースのSG
resource "aws_security_group" "main" {
name = "main_sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "test-resource-sg"
}
}
resource "aws_vpc_security_group_egress_rule" "egress_main" {
security_group_id = aws_security_group.main.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "-1"
}
# エンドポイントのSG
resource "aws_security_group" "endpoint" {
name = "endpoint"
vpc_id = aws_vpc.main.id
tags = {
Name = "endpoint-sg"
}
}
resource "aws_vpc_security_group_ingress_rule" "ingress_endpoint" {
security_group_id = aws_security_group.endpoint.id
referenced_security_group_id = aws_security_group.main.id
ip_protocol = "-1"
}
resource "aws_vpc_security_group_egress_rule" "egress_endpoint" {
security_group_id = aws_security_group.endpoint.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "-1"
}
# エンドポイント
## S3(Gateway)
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.s3"
tags = {
Name = "s3-endpoint"
}
}
resource "aws_vpc_endpoint_route_table_association" "s3" {
route_table_id = aws_route_table.private.id
vpc_endpoint_id = aws_vpc_endpoint.s3.id
}
## ECR
resource "aws_vpc_endpoint" "ecr_api" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ecr.api"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = [aws_subnet.private.id]
security_group_ids = [aws_security_group.endpoint.id]
tags = {
Name = "ecr-api-endpoint"
}
}
resource "aws_vpc_endpoint" "ecr_dkr" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ecr.dkr"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = [aws_subnet.private.id]
security_group_ids = [aws_security_group.endpoint.id]
tags = {
Name = "ecr-dkr-endpoint"
}
}
## CloudWatch Logs(CloudWatch Logsにログ出力する場合)
resource "aws_vpc_endpoint" "logs" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.logs"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = [aws_subnet.private.id]
security_group_ids = [aws_security_group.endpoint.id]
tags = {
Name = "logs-endpoint"
}
}
## Secrets Manager(Secrets Managerの値を利用する場合)
resource "aws_vpc_endpoint" "secretsmanager" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.secretsmanager"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = [aws_subnet.private.id]
security_group_ids = [aws_security_group.endpoint.id]
tags = {
Name = "secretsmanager-endpoint"
}
}
## Systems Manager(ECS ExecとSSMパラメータを利用する場合)
resource "aws_vpc_endpoint" "ssm" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ssm"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = [aws_subnet.private.id]
security_group_ids = [aws_security_group.endpoint.id]
tags = {
Name = "ssm-endpoint"
}
}
resource "aws_vpc_endpoint" "ssmmessages" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.ap-northeast-1.ssmmessages"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = [aws_subnet.private.id]
security_group_ids = [aws_security_group.endpoint.id]
tags = {
Name = "ssmmessages-endpoint"
}
}
使い方
リソース作成
リソースを作成するときは、普通にデプロイして問題ないです。
記述が誤っていて、importしているリソースに差分が発生していないかは要確認です。
terraform init
terraform apply
リソース削除
リソースを削除するときは、単純にdestroyしようとするとインポートしたVPC・サブネット・ルートテーブルも削除対象となってしまいます。
(prevent_destroy = true
を入れているため、destroyしようとするとエラーが出るようにはなっています。)
そのため、以下を実施します。
- importブロックのコメントアウト
- 既存リソースを管理対象から除外
terraform state rm aws_subnet.private
terraform state rm aws_route_table.private
terraform state rm aws_vpc.main
その後に、以下コマンドを実行するとインポートしたリソースは削除対象から外れて、エンドポイントのみを削除することができます。
$ terraform destroy
Destroy complete! Resources: 14 destroyed.
参考
最後に
今回は、ECS(Fargate)をプライベートサブネットで使うときに必要なエンドポイントを、Terraformで作れるようにしてみたことを記事にしました。
どなたかの参考になると幸いです。